package com.tjc.budge;

import java.time.LocalDate;
import java.time.YearMonth;
import java.util.List;
import java.util.stream.Collectors;

import static java.time.temporal.ChronoUnit.DAYS;

/**
 * @Author: tjc
 * @description
 * @Date Created in 23:54 2022-02-09
 */
public class BudgetPlan {

    private BudgetRepo repo;

    public BudgetPlan(BudgetRepo repo) {
        this.repo = repo;
    }

    public long query(LocalDate startDate, LocalDate endDate) {
        if (startDate.withDayOfMonth(1).equals(endDate.withDayOfMonth(1))) {
            long amount = getBudgetAmount(startDate);
            long daysInPeriod = getBudgetDaysCount(startDate);
            long daysBetween = startDate.until(endDate, DAYS) + 1;
            return amount / daysInPeriod * daysBetween;
        }

        if (YearMonth.from(startDate).isBefore(YearMonth.from(endDate))) {
            long amountStartPeriod = getBudgetAmount(startDate);
            long daysInStartPeriod = getBudgetDaysCount(startDate);
            long daysAfterStartDateInStartPeriod = startDate.until(startDate.withDayOfMonth(startDate.lengthOfMonth()), DAYS) + 1;
            long totalStartPeriod = amountStartPeriod / daysInStartPeriod * daysAfterStartDateInStartPeriod;

            long totalInMiddle = 0;
            for (Budget budget : getBudgetsBetween(startDate, endDate)) {
                totalInMiddle += budget.getAmount();
            }

            long amountEndPeriod = getBudgetAmount(endDate);
            long daysInEndPeriod = getBudgetDaysCount(endDate);
            long daysBeforeEndDateInEndPeriod = endDate.getDayOfMonth();
            long totalEndPeriod = amountEndPeriod / daysInEndPeriod * daysBeforeEndDateInEndPeriod;
            return totalStartPeriod + totalInMiddle + totalEndPeriod;
        }
        throw new RuntimeException("You should not be here. We have returned all legittimate numbers from getAmount(Date, Date) in Budget");
    }

    private List<Budget> getBudgetsBetween(LocalDate startDate, LocalDate endDate) {
        List<Budget> budgets = repo.findAll();
        return budgets.stream().filter(budget -> budget.getMonth().atDay(1).isAfter(startDate) && budget.getMonth().atEndOfMonth().isBefore(endDate)).collect(Collectors.toList());

    }

    private long getBudgetDaysCount(LocalDate date) {
        Budget budget = getBudgetContaining(date);
        return budget.getMonth().lengthOfMonth();
    }

    private Budget getBudgetContaining(LocalDate date) {
        List<Budget> budgets = repo.findAll();
        return budgets.stream().filter(budget -> budget.getMonth().atDay(1).equals(date.withDayOfMonth(1))).findFirst().orElse(new Budget(YearMonth.of(date.getYear(), date.getMonth()), 0));
    }

    private long getBudgetAmount(LocalDate date) {
        Budget budget = getBudgetContaining(date);
        return budget.getAmount();
    }
}

